#include "precomp.h"
#include "jconcurrentexception.h"

namespace JConcurrent {

/**
 * @brief Exception::raise
 */
void Exception::raise() const
{
    Exception e = *this;
    throw e;
}

/**
 * @brief Exception::clone
 * @return
 */
Exception *Exception::clone() const
{
    return new Exception(*this);
}

/**
 * @brief UnhandledException::raise
 */
void UnhandledException::raise() const
{
    UnhandledException e = *this;
    throw e;
}

/**
 * @brief UnhandledException::clone
 * @return
 */
Exception *UnhandledException::clone() const
{
    return new UnhandledException(*this);
}

/**
 *
 */
namespace internal {

/**
 * @brief The Base class
 */
class Base
{
public:
    /**
     * @brief Base
     * @param exception
     */
    Base(Exception *exception)
    : exception(exception), refCount(1), hasThrown(false) { }
    ~Base() { delete exception; }

    Exception *exception;
    QAtomicInt refCount;
    bool hasThrown;
};

/**
 * @brief ExceptionHolder::ExceptionHolder
 * @param exception
 */
ExceptionHolder::ExceptionHolder(Exception *exception)
: base(new Base(exception)) {}

/**
 * @brief ExceptionHolder::ExceptionHolder
 * @param other
 */
ExceptionHolder::ExceptionHolder(const ExceptionHolder &other)
: base(other.base)
{
    base->refCount.ref();
}

/**
 * @brief ExceptionHolder::operator =
 * @param other
 */
void ExceptionHolder::operator=(const ExceptionHolder &other)
{
    if (base == other.base)
        return;

    if (base->refCount.deref() == false)
        delete base;

    base = other.base;
    base->refCount.ref();
}

/**
 * @brief ExceptionHolder::~ExceptionHolder
 */
ExceptionHolder::~ExceptionHolder()
{
    if (base->refCount.deref() == 0)
        delete base;
}

/**
 * @brief ExceptionHolder::exception
 * @return
 */
Exception *ExceptionHolder::exception() const
{
    return base->exception;
}

/**
 * @brief ExceptionStore::setException
 * @param e
 */
void ExceptionStore::setException(const Exception &e)
{
    if (hasException() == false)
        exceptionHolder = ExceptionHolder(e.clone());
}

/**
 * @brief ExceptionStore::hasException
 * @return
 */
bool ExceptionStore::hasException() const
{
    return (exceptionHolder.exception() != 0);
}

/**
 * @brief ExceptionStore::exception
 * @return
 */
ExceptionHolder ExceptionStore::exception()
{
    return exceptionHolder;
}

/**
 * @brief ExceptionStore::throwPossibleException
 */
void ExceptionStore::throwPossibleException()
{
    if (hasException() ) {
        exceptionHolder.base->hasThrown = true;
        exceptionHolder.exception()->raise();
    }
}

/**
 * @brief ExceptionStore::hasThrown
 * @return
 */
bool ExceptionStore::hasThrown() const { return exceptionHolder.base->hasThrown; }

} // namespace internal

} // namespace JConcurrent
